package com.battle.service.interceptor;

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * @author anni.zhu
 * @author anni.zhu@king-v.com
 * Created in 2024/11/25 15:46
 */
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args =
        {MappedStatement.class, Object.class})})
public class PrintSqlInterceptor implements InnerInterceptor{
    private static final Logger logger = LoggerFactory.getLogger (PrintSqlInterceptor.class);

    /**
     * Before query.
     *
     * @param executor      Executor(maybe a proxy object)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
                            ResultHandler resultHandler, BoundSql boundSql) {
        printSql (ms, boundSql);
    }

    /**
     * Before update.
     *
     * @param executor  Executor(maybe a proxy object)
     * @param ms        MappedStatement
     * @param parameter parameter
     */
    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
        BoundSql boundSql = ms.getBoundSql (parameter);
        printSql (ms, boundSql);
    }

    /**
     * Print SQL
     *
     * @param ms       MappedStatement
     * @param boundSql BoundSql
     */
    private void printSql(MappedStatement ms, BoundSql boundSql) {

        Object parameterObject = boundSql.getParameterObject ();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings ();
        Configuration configuration = ms.getConfiguration ();
        //replace all whitespace characters with a single space
        String sql = boundSql.getSql ().replaceAll ("[\\s]+", " ");
        if (!parameterMappings.isEmpty () && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry ();
            if (typeHandlerRegistry.hasTypeHandler (parameterObject.getClass ())) {
                sql = sql.replaceFirst ("\\?", getParameterValue (parameterObject));
            } else {
                MetaObject metaObject = configuration.newMetaObject (parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty ();
                    if (metaObject.hasGetter (propertyName)) {
                        Object obj = metaObject.getValue (propertyName);
                        sql = sql.replaceFirst ("\\?", getParameterValue (obj));
                    } else if (boundSql.hasAdditionalParameter (propertyName)) {
                        Object obj = boundSql.getAdditionalParameter (propertyName);
                        sql = sql.replaceFirst ("\\?", getParameterValue (obj));
                    }
                }
            }
        }
        logger.info ("Print SQL");
        logger.info ("SQL: {}", sql);
    }

    /**
     * get parameter value
     *
     * @param obj parameter object
     * @return parameter value
     */
    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString () + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance (DateFormat.DEFAULT, DateFormat.DEFAULT,
                    Locale.CHINA);
            value = "'" + formatter.format (new Date ()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString ();
            } else {
                value = "";
            }
        }
        return value.replace ("$", "\\$");
    }
}
